package com.gitee.l0km.codegen.decorator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.l0km.codegen.base.generator.GeneratorConfiguration;
import com.google.common.base.Predicate;
import com.google.common.collect.Sets;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.MoreObjects.firstNonNull;

/**
 * 根据includeMethods,excludeMethods过滤参数对接口中的方法进行过滤
 * @author guyadong
 *
 */
public class MethodNameFilter implements Predicate<Method>{
	private static final Logger logger = LoggerFactory.getLogger(MethodNameFilter.class);

	private final Set<Method> includeMethods;
	private final Set<Method> excludeMethods;
	private final GeneratorConfiguration config;
	public MethodNameFilter(Class<?> inputClass,GeneratorConfiguration config) {
		checkArgument(inputClass != null,"inputClass is null");
		this.config = checkNotNull(config,"config is null");
		Iterable<Method> methods = Arrays.asList(inputClass.getMethods()); 
		excludeMethods = getExcludeMethodList(inputClass,methods);
		includeMethods = getIncludeMethodList(inputClass,methods);
		if(!includeMethods.isEmpty() && !excludeMethods.isEmpty()){
			logger.warn("includeMethods and excludeMethods all defined, this first used preferentially");
		}
	}
	private List<Method> findMethod(String pattern,Iterable<Method> methods,boolean startsWithAsTrue){
		List<Method> found = new ArrayList<>();		
		for(Method method:methods){
			if(PatternFilter.filter(pattern, method.getName(),startsWithAsTrue)){
				found.add(method);
			}
		}
		return found;
	}
	private Set<Method> getExcludeMethodList(Class<?> inputClass,Iterable<Method> methods){
		Set<Method> sets = Sets.newHashSet();
		List<String> names = firstNonNull(config.getExcludeMethods().get(inputClass),Collections.<String>emptyList());

		for(String name:names){
			List<Method> found = findMethod(name,methods,true);
			if(found.isEmpty()){
				logger.warn("NOT FOUND Method named '{}' in {}",name,inputClass.getName());				
				continue;
			}			
			sets.addAll(found);
		}
	
		return sets;
	}
	private Set<Method> getIncludeMethodList(Class<?> inputClass,Iterable<Method> methods){
		Set<Method> sets = Sets.newHashSet();
		List<String> names = firstNonNull(config.getIncludeMethods().get(inputClass),Collections.<String>emptyList());

		for(String name:names){
			List<Method> found = findMethod(name,methods,false);
			if(found.isEmpty()){
				logger.warn("NOT FOUND Method named '{}' in {}",name,inputClass.getName());				
				continue;
			}			
			sets.addAll(found);
		}
	
		return sets;
	}
	@Override
	public boolean apply(Method input) {
		if(!includeMethods.isEmpty()){
			return includeMethods.contains(input);
		}
		if(!excludeMethods.isEmpty()){
			return !excludeMethods.contains(input);
		}
		return true;
	}
}
